package main

import (
	"fmt"
	"sort"
	"strconv"
	"strings"
)

// 1、求n的阶乘。至少使用递归函数完成一次。
// 循环版
func factorialLoop(n int) int {
	if n <= 0 {
		panic("n必须大于0")
	}
	f := 1
	for i := n; i > 0; i-- {
		f *= i
	}
	return f
}

// 循环转递归版
func factorialLoopToRecursion(n, f int) int {
	if n <= 0 {
		panic("n必须大于0")
	}
	if n == 1 {
		return f
	}
	f = n * f
	f = factorialLoopToRecursion(n-1, f)
	return f
}

// 递归版
func factorialRecursion(n int) int {
	if n <= 0 {
		panic("n必须大于0")
	}
	if n == 1 {
		return 1
	}
	return n * factorialLoop(n-1)
}

// 2、编写一个函数，接受一个参数n，n为正整数。要求数字必须对齐
func triangleFlip(n int) {
	if n <= 0 {
		panic("n必须大于0")
	}
	m := initArr(n)
	nLen := len(strconv.Itoa(n))
	for i := 0; i < n; i++ {
		for j := 0; j <= i; j++ {
			m[i][j] = j + 1
		}
		sort.Sort(sort.Reverse(sort.IntSlice(m[i])))
		var s strings.Builder
		for j := 0; j < n; j++ {
			if m[i][j] == n+1 {
				fmt.Fprintf(&s, "%*s ", nLen, "")
			} else {
				fmt.Fprintf(&s, "%*d ", nLen, m[i][j])
			}
		}
		fmt.Println(s.String())
	}
}

func initArr(n int) [][]int {
	m := make([][]int, 0, n)
	for i := 0; i < n; i++ {
		temp := make([]int, 0, n)
		for j := 0; j < n; j++ {
			temp = append(temp, n+1)
		}
		m = append(m, temp)
	}
	return m
}

// 原方案优化
func triangleFlip2(n int)  {
	if n <= 0 {
		panic("n必须大于0")
	}
	nLen := len(strconv.Itoa(n))
	for i := 0; i < n; i++ {
		m := make([]int, n)
		for j := 0; j <= i; j++ {
			m[n-j-1] = j + 1
		}
		var s strings.Builder
		for j := 0; j < n; j++ {
			if m[j] == 0 {
				fmt.Fprintf(&s, "%*s ", nLen, "")
			} else {
				fmt.Fprintf(&s, "%*d ", nLen, m[j])
			}
		}
		fmt.Println(s.String())
	}
}

// 借鉴17030李浩同学的方案再写一个
func triangleCopy(n int) {
	var b strings.Builder
	for i := n; i >= 1; i-- {
		fmt.Fprintf(&b, "%*d\x20", len(strconv.Itoa(n)), i)
	}
	str := strings.TrimRight(b.String(), "\x20")
	width := len(str)
	spaceNum := 0
	for i := width - 1; i >= 0; i-- {
		if str[i] == ' ' && spaceNum == 0 {
			spaceNum++
			fmt.Printf("%*s\n", width, str[i+1:])
		}
		if str[i] != ' ' {
			spaceNum = 0
		}
		if i == 0 {
			fmt.Println(str)
		}
	}
}

// 3、简述defer作用和应用
// defer语法可以延迟执行函数，它会在函数返回和panic时执行，多个defer会按照后进先出(LIFO)的规则一次执行，但是如果函数中有os.Exit则不会执行defer后的函数。
// defer后的函数如果使用了参数，那么会在注册时确定好参数的值
// 应用：
//	(1) 释放资源，如：mysql、redis、file等资源，在函数结束或者pnic时自动帮我们关闭资源，减少内存消耗
//	(2) 处理错误，在产生panic错误时，通过defer可以记录日志或者恢复，帮助我们定位错误或者保持程序运行
//	(3) 对命名的返回值进行处理
